/*
 * @Author: ZhengJie
 * @Date: 2023-05-05 23:07:06
 * @Description: 雪花算法+时间回拨
 */

export interface ISnowflakeIDOptions {
  Method: bigint | number; // 雪花计算方法，（1-漂移算法|2-传统算法），默认 1
  BaseTime: bigint | number; // 基础时间（ms 单位），不能超过当前系统时间
  WorkerId: bigint | number; // 机器码，必须由外部设定，最大值 2^WorkerIdBitLength-1
  WorkerIdBitLength: bigint | number; // 机器码位长，默认值 6，取值范围 [1, 15](要求：序列数位长+机器码位长不超过 22)
  SeqBitLength: bigint | number; // 序列数位长，默认值 6，取值范围 [3, 21](要求：序列数位长+机器码位长不超过 22)
  MaxSeqNumber: bigint | number; // 最大序列数（含），设置范围 [MinSeqNumber, 2^SeqBitLength-1]，默认值 0，表示最大序列数取最大值（2^SeqBitLength-1]）
  MinSeqNumber: bigint | number; // 最小序列数（含），默认值 5，取值范围 [5, MaxSeqNumber]，每毫秒的前 5 个序列数对应编号 0-4 是保留位，其中 1-4 是时间回拨相应预留位，0 是手工新值预留位
  TopOverCostCount: bigint | number; // 最大漂移次数（含），默认 2000，推荐范围 500-10000（与计算能力有关）
}

export default class SnowflakeID {
  // 雪花计算方法，（1-漂移算法|2-传统算法），默认 1
  Method: bigint | number;

  // 基础时间（ms 单位），不能超过当前系统时间
  BaseTime: bigint | number;

  // 机器码，必须由外部设定，最大值 2^WorkerIdBitLength-1
  WorkerId: bigint | number;

  // 机器码位长，默认值 6，取值范围 [1, 15](要求：序列数位长+机器码位长不超过 22)
  WorkerIdBitLength: bigint | number;

  // 序列数位长，默认值 6，取值范围 [3, 21](要求：序列数位长+机器码位长不超过 22)
  SeqBitLength: bigint | number;

  // 最大序列数（含），设置范围 [MinSeqNumber, 2^SeqBitLength-1]，默认值 0，表示最大序列数取最大值（2^SeqBitLength-1]）
  MaxSeqNumber: bigint | number;

  // 最小序列数（含），默认值 5，取值范围 [5, MaxSeqNumber]，每毫秒的前 5 个序列数对应编号 0-4 是保留位，其中 1-4 是时间回拨相应预留位，0 是手工新值预留位
  MinSeqNumber: bigint | number;

  // 最大漂移次数（含），默认 2000，推荐范围 500-10000（与计算能力有关）
  TopOverCostCount: bigint | number;

  _TimestampShift: any;
  _CurrentSeqNumber: any;
  _LastTimeTick: any;
  _TurnBackTimeTick: any;
  _TurnBackIndex: any;
  _OverCostCountInOneTerm: any;
  _IsOverCost: boolean;

  /**
   *Creates an instance of Genid.
   * @param {{
   *     Method: 1, // 雪花计算方法，（1-漂移算法|2-传统算法），默认 1
   *     BaseTime: 1577836800000,  // 基础时间（ms 单位），不能超过当前系统时间
   *     WorkerId: Number, // 机器码，必须由外部设定，最大值 2^WorkerIdBitLength-1
   *     WorkerIdBitLength: 6,   // 机器码位长，默认值 6，取值范围 [1, 15](要求：序列数位长+机器码位长不超过 22)
   *     SeqBitLength: 6,   // 序列数位长，默认值 6，取值范围 [3, 21](要求：序列数位长+机器码位长不超过 22)
   *     MaxSeqNumber: 5, // 最大序列数（含），设置范围 [MinSeqNumber, 2^SeqBitLength-1]，默认值 0，表示最大序列数取最大值（2^SeqBitLength-1]）
   *     MinSeqNumber: 5, // 最小序列数（含），默认值 5，取值范围 [5, MaxSeqNumber]，每毫秒的前 5 个序列数对应编号 0-4 是保留位，其中 1-4 是时间回拨相应预留位，0 是手工新值预留位
   *     TopOverCostCount: 2000// 最大漂移次数（含），默认 2000，推荐范围 500-10000（与计算能力有关）
   * }} options
   * @memberof Genid
   */
  constructor(options: ISnowflakeIDOptions) {
    if (options.WorkerId === undefined) {
      throw new Error('lost WorkerId');
    }
    // 1.BaseTime
    const BaseTime = 1577836800000;
    if (!options.BaseTime || options.BaseTime < 0) {
      options.BaseTime = BaseTime;
    }
    // 2.WorkerIdBitLength
    const WorkerIdBitLength = 6;
    if (!options.WorkerIdBitLength || options.WorkerIdBitLength < 0) {
      options.WorkerIdBitLength = WorkerIdBitLength;
    }

    // 4.SeqBitLength
    const SeqBitLength = 6;
    if (!options.SeqBitLength || options.SeqBitLength < 0) {
      options.SeqBitLength = SeqBitLength;
    }
    // 5.MaxSeqNumber
    const MaxSeqNumber = (1 << (options.SeqBitLength as number)) - 1;
    if (options.MaxSeqNumber <= 0 || options.MaxSeqNumber === undefined) {
      options.MaxSeqNumber = MaxSeqNumber;
    }
    // 6.MinSeqNumber
    const MinSeqNumber = 5;
    if (!options.MinSeqNumber || options.MinSeqNumber < 0) {
      options.MinSeqNumber = MinSeqNumber;
    }
    // 7.Others
    const topOverCostCount = 2000;
    if (!options.TopOverCostCount || options.TopOverCostCount < 0) {
      options.TopOverCostCount = topOverCostCount;
    }

    if (options.Method !== 2) {
      options.Method = 1;
    } else {
      options.Method = 2;
    }

    this.Method = BigInt(options.Method);
    this.BaseTime = BigInt(options.BaseTime);
    this.WorkerId = BigInt(options.WorkerId);
    this.WorkerIdBitLength = BigInt(options.WorkerIdBitLength);
    this.SeqBitLength = BigInt(options.SeqBitLength);
    this.MaxSeqNumber = BigInt(options.MaxSeqNumber);
    this.MinSeqNumber = BigInt(options.MinSeqNumber);
    this.TopOverCostCount = BigInt(options.TopOverCostCount);

    const timestampShift = this.WorkerIdBitLength + this.SeqBitLength;
    const currentSeqNumber = this.MinSeqNumber;

    this._TimestampShift = timestampShift;
    this._CurrentSeqNumber = currentSeqNumber;

    this._LastTimeTick = 0;
    this._TurnBackTimeTick = 0;
    this._TurnBackIndex = 0;
    this._IsOverCost = false;
    this._OverCostCountInOneTerm = 0;
  }

  // DoGenIDAction .
  DoGenIdAction(OverCostActionArg) {
    // console.log('DoGenIdAction');
  }

  BeginOverCostAction(useTimeTick) {
    // console.log('BeginOverCostAction');
  }

  EndOverCostAction(useTimeTick) {
    // if m1._TermIndex > 10000 {
    //     m1._TermIndex = 0
    // }
  }

  BeginTurnBackAction(useTimeTick) {
    // console.log('BeginTurnBackAction');
  }

  EndTurnBackAction(useTimeTick) {
    // console.log('EndTurnBackAction');
  }

  NextOverCostId() {
    const currentTimeTick = this.GetCurrentTimeTick();
    if (currentTimeTick > this._LastTimeTick) {
      // this.EndOverCostAction(currentTimeTick)
      this._LastTimeTick = currentTimeTick;
      this._CurrentSeqNumber = this.MinSeqNumber;
      this._IsOverCost = false;
      this._OverCostCountInOneTerm = 0;
      // this._GenCountInOneTerm = 0
      return this.CalcId(this._LastTimeTick);
    }
    if (this._OverCostCountInOneTerm >= this.TopOverCostCount) {
      // this.EndOverCostAction(currentTimeTick)
      this._LastTimeTick = this.GetNextTimeTick();
      this._CurrentSeqNumber = this.MinSeqNumber;
      this._IsOverCost = false;
      this._OverCostCountInOneTerm = 0;
      // this._GenCountInOneTerm = 0
      return this.CalcId(this._LastTimeTick);
    }
    if (this._CurrentSeqNumber > this.MaxSeqNumber) {
      this._LastTimeTick++;
      this._CurrentSeqNumber = this.MinSeqNumber;
      this._IsOverCost = true;
      this._OverCostCountInOneTerm++;
      // this._GenCountInOneTerm++

      return this.CalcId(this._LastTimeTick);
    }

    // this._GenCountInOneTerm++
    return this.CalcId(this._LastTimeTick);
  }

  NextNormalId() {
    const currentTimeTick = this.GetCurrentTimeTick();
    if (currentTimeTick < this._LastTimeTick) {
      if (this._TurnBackTimeTick < 1) {
        this._TurnBackTimeTick = (this._LastTimeTick as number) - 1;
        this._TurnBackIndex++;
        // 每毫秒序列数的前 5 位是预留位，0 用于手工新值，1-4 是时间回拨次序
        // 支持 4 次回拨次序（避免回拨重叠导致 ID 重复），可无限次回拨（次序循环使用）。
        if (this._TurnBackIndex > 4) {
          this._TurnBackIndex = 1;
        }
        this.BeginTurnBackAction(this._TurnBackTimeTick);
      }

      return this.CalcTurnBackId(this._TurnBackTimeTick);
    }

    // 时间追平时，_TurnBackTimeTick 清零
    if (this._TurnBackTimeTick > 0) {
      this.EndTurnBackAction(this._TurnBackTimeTick);
      this._TurnBackTimeTick = 0;
    }

    if (currentTimeTick > this._LastTimeTick) {
      this._LastTimeTick = currentTimeTick;
      this._CurrentSeqNumber = this.MinSeqNumber;
      return this.CalcId(this._LastTimeTick);
    }

    if (this._CurrentSeqNumber > this.MaxSeqNumber) {
      this.BeginOverCostAction(currentTimeTick);
      // this._TermIndex++
      this._LastTimeTick++;
      this._CurrentSeqNumber = this.MinSeqNumber;
      this._IsOverCost = true;
      this._OverCostCountInOneTerm = 1;
      // this._GenCountInOneTerm = 1

      return this.CalcId(this._LastTimeTick);
    }

    return this.CalcId(this._LastTimeTick);
  }

  CalcId(useTimeTick) {
    const result =
      BigInt(useTimeTick << this._TimestampShift) +
      BigInt((this.WorkerId as bigint) << (this.SeqBitLength as bigint)) +
      BigInt(this._CurrentSeqNumber);
    this._CurrentSeqNumber++;
    return result;
  }

  CalcTurnBackId(useTimeTick) {
    const result =
      BigInt(useTimeTick << this._TimestampShift) +
      BigInt((this.WorkerId as bigint) << (this.SeqBitLength as bigint)) +
      BigInt(this._TurnBackIndex);
    this._TurnBackTimeTick--;
    return result;
  }

  GetCurrentTimeTick() {
    const millis = BigInt(new Date().valueOf());
    return millis - (this.BaseTime as bigint);
  }

  GetNextTimeTick() {
    let tempTimeTicker = this.GetCurrentTimeTick();
    while (tempTimeTicker <= this._LastTimeTick) {
      tempTimeTicker = this.GetCurrentTimeTick();
    }
    return tempTimeTicker;
  }

  /**
   * 生成ID
   * @returns 始终输出number类型，超过时throw error
   */
  NextNumber() {
    if (this._IsOverCost) {
      //
      const id = this.NextOverCostId();
      if (id >= 9007199254740992n)
        throw Error(`${id.toString()} over max of Number 9007199254740992`);

      return parseInt(id.toString());
    } else {
      //
      const id = this.NextNormalId();
      if (id >= 9007199254740992n)
        throw Error(`${id.toString()} over max of Number 9007199254740992`);

      return parseInt(id.toString());
    }
  }

  /**
   * 生成ID
   * @returns 根据输出数值判断，小于number最大值时输出number类型，大于时输出bigint
   */
  NextId(): number | bigint {
    if (this._IsOverCost) {
      const id = this.NextOverCostId();
      if (id >= 9007199254740992n) return id;
      else return parseInt(id + '');
    } else {
      const id = this.NextNormalId();
      if (id >= 9007199254740992n) return id;
      else return parseInt(id + '');
    }
  }

  /**
   * 生成ID
   * @returns 始终输出bigint类型
   */
  NextBigId() {
    if (this._IsOverCost) {
      //
      return this.NextOverCostId();
    } else {
      //
      return this.NextNormalId();
    }
  }
}
